﻿using System;
using System.Net;
using System.Collections.Generic;

namespace NATP2P.STUN
{

    /* class StunServers
     {

         static private List<IPEndPoint> _server;
         /// <summary>
         /// Get List Available Stun Server
         /// </summary>
         static public List<IPEndPoint> Server
         {
             get
             {
                 if (_server == null)
                 {
                     _server = new List<IPEndPoint>();
                     Server.Add(new IPEndPoint(Dns.GetHostEntry("stunserver.org").AddressList[0], 3478));
                     Server.Add(new IPEndPoint(Dns.GetHostEntry("stun.callwithus.com").AddressList[0], 3478));
                     Server.Add(new IPEndPoint(Dns.GetHostEntry("stun.gmx.net").AddressList[0], 3478));
                     Server.Add(new IPEndPoint(Dns.GetHostEntry("stun.endigovoip.com").AddressList[0], 3478));
                 }
                 return _server;
             }
             set { _server = value; }
         }

         /// <summary>
         /// Default constructor
         /// </summary>
         public StunServers()
         {



         }


     }*/
    /// <summary>
    /// Represent Minimum Size Of Packet Message
    /// </summary>
    class Packet
    {

        Guid _randomID;
        byte[] _UniqueIDTransaction = new byte[16];



        public Packet()
        {

        }
        /// <summary>
        /// Create empty Packet Contain necessary Information
        /// </summary>
        /// <returns></returns>
        public byte[] CreatePacket()
        {
            _randomID = Guid.NewGuid();
            _UniqueIDTransaction = _randomID.ToByteArray();
            byte[] packet = new byte[28]
            { 0, 1, 0, 8, 
              0, 0, 0, 0,
              0, 0, 0, 0,
              0, 0, 0, 0, 
              0, 0, 0, 0,
              0, 3, 0, 4, 
              0, 0, 0, 0 };

            for (int i = 0; i < 16; i++)
                packet[i + 4] = _UniqueIDTransaction[i];
            return packet;

        }

        /// <summary>
        /// Extract The Information From Message Response
        /// </summary>
        /// <param name="packet"></param>
        /// <returns>External IP Address And External Port</returns>
        public IPEndPoint ParsePacket(byte[] packet)
        {
            if (packet.Length < 20)
                throw new Exception("STUN response too short to have a header");
            if (packet[0] != 1 || packet[1] != 1)  //0x0101	Binding Response.
                throw new Exception("STUN Reposne has to be wrong type, must Binding Response");
            for (int i = 0; i < 16; i++)
                if (packet[i + 4] != _UniqueIDTransaction[i])
                    throw new Exception("STUN response has incorrect transaction ID");
            int size = packet[2] * 256 + packet[3];
            int pos = 20;
            for (; ; )
            {
                if (size < 4)
                    throw new Exception("Remaining message too short not to further attribute header, " + "mapped address attribute found");
                int attrsize = packet[pos + 2] * 256 + packet[pos + 3];
                if (pos + 4 + attrsize > packet.Length)
                    throw new Exception("Attribute header has length that does not fit into the package");
                //If no mapped address attribute then skip
                if (packet[pos] != 0 || packet[pos + 1] != 1)
                {
                    pos += attrsize;
                    continue;
                }
                if (attrsize < 8)
                    throw new Exception("Mapped Address attribute is too short");
                if (packet[pos + 5] != 1)
                    throw new Exception("Address of mapped address is not IPV4");
                int port = packet[pos + 6] * 256 + packet[pos + 7];
                IPAddress adress = new IPAddress(new byte[4] { packet[pos + 8], packet[pos + 9], packet[pos + 10], packet[pos + 11] });
                return new IPEndPoint(adress, port);
            }
        }
    }
}
